package cii_lib.drivers;
import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;

import java.io.InputStream;
import java.io.OutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ktpUART {
	
	private static final Logger log = LoggerFactory.getLogger(ktpUART.class);

 private SerialPort serialPort = null;
 private InputStream in = null;
 private OutputStream out = null;
 private int II_ADDR_BYTES;
 private int II_DATA_BYTES;

 private ktpUART(){ }

 public static ktpUART connect (String name, int baud , int stopbits, int addr_len, int data_len) throws Exception{

   ktpUART UART = new ktpUART();

   UART.II_ADDR_BYTES = (addr_len-1)/8+1;
   if(UART.II_ADDR_BYTES<1 || UART.II_ADDR_BYTES>4)return(null);
   UART.II_DATA_BYTES = (data_len-1)/8+1;
   if(UART.II_DATA_BYTES<1 || UART.II_DATA_BYTES>4)return(null);

   CommPortIdentifier portIdentifier = CommPortIdentifier.getPortIdentifier(name);
   if ( portIdentifier.isCurrentlyOwned() ) return (null);
   CommPort commPort = portIdentifier.open(UART.getClass().getName(),2000);

   if ( commPort instanceof SerialPort ) {
     UART.serialPort = (SerialPort) commPort;
     UART.serialPort.setSerialPortParams(baud,SerialPort.DATABITS_8,stopbits,SerialPort.PARITY_NONE);

     UART.in = UART.serialPort.getInputStream();
     UART.out = UART.serialPort.getOutputStream();
     return (UART);
   }
   else return(null);
 }

 /**
 * @return the serialPort
 */
public SerialPort getSerialPort() {
	return serialPort;
}

private boolean clean() throws Exception{

   boolean c=false;
   while(in.read()>=0)
     c=true;
   return(c);
 }

 private int b2i(byte b){
   return(b<0?b+256:b);
 }

 public boolean reset() throws Exception{
   System.out.print("reset: ");

   clean();

   serialPort.setRTS(true);
   while(serialPort.isCTS()==false);
   serialPort.setRTS(false);

   int r, s;
   s=0x80;
   byte[] w = new byte[1];
   w[0]=(byte)s;
   for (int i=0;i<16;i++){
     out.write(w);
     r=in.read();
     if(s!=r){
       System.out.print(" stage1 error:"+s+"->"+r+";\n");
       return(false);
     }
   }
   while(serialPort.isCTS()==true);

   s=1;
   for (int i=0;i<1000;i++){
     w[0]=(byte)s;
     out.write(w);
     while((r=in.read())<0);
     if(s!=r){
       System.out.print(" stage2 error:"+s+"->"+r+";\n");
       return(false);
     }
     System.out.print(r+";");
     s=(s+13)%256;
     if(r==0)
       break;
   }

   if (clean()==true){
     System.out.print(" stage3 error\n");
     return(false);
   }

   System.out.print('\n');
   return(true);
 }

 public int ack() throws Exception{
   System.out.print("ack: ");

   int r;
   while((r=in.read())<0);
   if (clean()==true){
     System.out.print(" stage1 error\n");
     return(0);
   }

   System.out.print(r+"\n");
   return(r);
 }

 public boolean send(byte[] data, int len) throws Exception{
   System.out.print("send: ");

   if (len<1 || len>14 || len>data.length){
     System.out.print(" data length = ("+len+") error\n");
     return(false);
   }

   if (clean()==true){
     System.out.print(" stage1 error\n");
     return(false);
   }

   byte[] h = new byte[1];
   h[0]=0;
   byte[] datainv = new byte[len];
   for (int index=0; index<len; index++){
     h[0]=(byte)((h[0]+data[len-index-1])%256);
     datainv[index]=data[len-index-1];
   }
   h[0]=(byte)((((h[0]>>4)%16+h[0]%16+len)%16)*16+len);
   System.out.print(b2i(h[0]));
   out.write(h);
   out.write(datainv);
   for (int index=0; index<len; index++){
     System.out.print(", "+b2i(datainv[index]));
   }

   System.out.print('\n');
   return(true);
 }

 public boolean send(byte[] data) throws Exception{
   return(send(data,data.length));
 }

 public int receive(byte[] data) throws Exception{
   System.out.print("receive: ");

   int l=data.length;
   if (l==0 || l>14){
     System.out.print(" data length = ("+l+") error\n");
     return(0);
   }

   byte[] h = new byte[1];
   h[0]=(byte)(1*16);
   out.write(h);

   int r;
   while((r=in.read())<0);
   System.out.print(r);
   h[0]=(byte)r;

   l=r%16;
   if (l==0 || l>14){
     System.out.print(" receive length = ("+l+") error\n");
     return(0);
   }
   byte c=0;
   for (int i=0;i<l;i++){
     while((r=in.read())<0);
     System.out.print(", "+r);
     data[i]=(byte)r;
     c = (byte)((c+r)%256);
   }

   c=(byte)((((c>>4)%16+c%16+l)%16)*16+l);
   if (c!=h[0]){
     System.out.print(" stage1 error:"+b2i(c)+"!="+b2i(h[0])+";\n");
     return(0);
   }

   System.out.print('\n');
   return(l);
 }

 public boolean ii_write(long addr, long data) throws Exception{

   System.out.print("ii_write: addr="+addr+", data="+data+"\n");
   byte[] d = new byte[II_ADDR_BYTES+II_DATA_BYTES];
   for (int i=0; i<II_ADDR_BYTES; i++){
     d[i]=(byte)(addr&0xff);
     addr>>=8;
   }
   for (int i=0; i<II_DATA_BYTES; i++){
     d[II_ADDR_BYTES+i]=(byte)(data&0xff);
     data>>=8;
   }
   if (send(d)==false) return(false);
   if (ack()!=33) return(false);
   System.out.print("ii_write: ok\n");
   return(true);
 }

 public long ii_read(int addr) throws Exception{

   System.out.print("ii_read: addr="+addr+"\n");
   byte[] a = new byte[II_ADDR_BYTES];
   for (int i=0; i<II_ADDR_BYTES; i++){
     a[i]=(byte)(addr&0xff);
     addr>>=8;
   }
   if (send(a)==false) return(-1);
   if (ack()!=77) return(-1);
   byte[] d = new byte[II_DATA_BYTES];
   if (receive(d)!=II_DATA_BYTES) return(-1);
   if (ack()!=33) return(-1);
   long data=0;
   for (int i=0; i<II_DATA_BYTES; i++){
     data+=b2i(d[i])<<(8*i);
   }
   System.out.print("ii_read: data="+data+"\n");
   return(data);
 }

 public static void main(String[] args){
   try
   {
     ktpUART UART = ktpUART.connect("COM15",921600,SerialPort.STOPBITS_1,16,16);
     if (UART.reset()==false) return;

     long d;
     if ((d=UART.ii_read(0))<0) System.exit(0);
     if ((d=UART.ii_read(1))<0) System.exit(0);
     if ((d=UART.ii_read(2))<0) System.exit(0);
     if ((d=UART.ii_read(3))<0) System.exit(0);
     if ((d=UART.ii_read(4))<0) System.exit(0);
     if (UART.ii_write(4,23456)==false) System.exit(0);
     if (UART.ii_write(5,12345)==false) System.exit(0);
     if ((d=UART.ii_read(4))<0) System.exit(0);
     if ((d=UART.ii_read(5))<0) System.exit(0);
     System.out.print("finish\n");
     System.exit(0);
   }
   catch ( Exception e )
   {
     log.error("", e);
   }
 }

}

